package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @Author: 李旭
 * @Time: 11:13
 * @Description:
 *
 *   全局过滤器
 *     校验是否登录
 *      自定义过滤器 GlobalLoginFilter
 *      默认执行 9大过滤器
 *      10大过滤器    1 2 3 4 5 6 7 8 9 10
 *
 */
@Component
public class GlobalLoginFilter implements GlobalFilter, Ordered {

    //配置URI路径
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    //浏览器中令牌的名称
    public static final String TOKEN = "token";
    //浏览器中临时用户的名称
    public static final String USERTEMPID = "userTempId";
    //RedisConfig配置类 创建RedisTemplate 不再是原生Redis客户端
    @Autowired
    private RedisTemplate redisTemplate;//格式有问题？ 有问题  K类型与V类型 格式
    @Value("${auth.url}")
    private String[] authUrl;
    private static final String LOGINURL = "http://passport.gmall.com/login.html?originUrl=";
    //全局过滤器执行方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // URL : http://cart.gmall.com/login.html    Tomcat Servlet Http请求
        // URI : /auth/...
        // path :
        //1：获取当前URI
        String path = request.getURI().getPath();
        //2:内部资源不允许浏览器访问   /**/inner/**  登录与未登录都不行
        if(antPathMatcher.match("/**/inner/**", path)){
            //是内部资源  无权访问
            return getVoidMono(response,ResultCodeEnum.PERMISSION);
        }
        //3:判断当前URI是否必须登录 /auth 开始就要登录了  否则不用登录
        String userId = getUserId(request);
        //3.1：异步  /**/auth/**  网关 直接连接Redis校验用户是否登录  未登录 直接返回值 201 登录了 放行
        if(antPathMatcher.match("/**/auth/**", path)){
            //当前Path是需要登录 判断用户是否登录
            if(StringUtils.isEmpty(userId)){
                //未登录
                // 弹出小窗口   ： 异步  Ajax  不刷新页面 异步
                return getVoidMono(response,ResultCodeEnum.LOGIN_AUTH);
            }
        }
        //3.2：同步 /trade.html /order.html  /pay.html  网关路由到后面页面微服务
        for (String url : authUrl) { //url  trade.html indexOf("/") = -1
            // path : /trade.html
            // url : /.indexOf(/trade.html) == -1
            if(path.indexOf(url) != -1 && StringUtils.isEmpty(userId)){
                //需要登录 再判断是否登录了  未登录
                // 直接转发登录页面  同步  window.location.href 刷新页面 同步
                //重定向
                //状态码
                response.setStatusCode(HttpStatus.SEE_OTHER);
                //URL地址
                try {
                    response.getHeaders().set(HttpHeaders.LOCATION,
                            LOGINURL + URLEncoder.encode(request.getURI().toString(),"utf-8")
                            );
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return response.setComplete();//完成之后可以响应了
            }
        }
        //加入购物车
        //当前是网关 传递用户ID给后面的微服务   请求头 K:userId V:3
        //request.getHeaders().set("userId",userId);
        //1）传递真实用户ID
        if(!StringUtils.isEmpty(userId)){
            request.mutate().header("userId", userId);
        }
        //2）传递临时用户ID
        String userTempId = getUserTempId(request);
        if(!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId", userTempId);
        }
        //放行
        return chain.filter(exchange);
    }

    private Mono<Void> getVoidMono(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> r = Result.build(null, resultCodeEnum);
        String result = JSONObject.toJSONString(r);
        DataBuffer dataBuffer = response.bufferFactory().wrap(result.getBytes());
        //解决Response乱码问题 Content-Type:application/json;charset=utf-8
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE,
                MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    //获取临时用户ID
    private String getUserTempId(ServerHttpRequest request) {
        //请求头  异步请求时获取方式
        String userTempId = request.getHeaders().getFirst(USERTEMPID);

        if(StringUtils.isEmpty(userTempId)){
            //同步请求时 获取方式
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if(null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }

    //判断用户是否  获取用户ID
    private String getUserId(ServerHttpRequest request) {
        //页面上异步Ajax请求  令牌放在请求头中  同步 请求时 令牌放在了Cookie中
        //1:获取令牌
        String token = request.getHeaders().getFirst(TOKEN);
        if(StringUtils.isEmpty(token)){
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if(null != httpCookie){
                token = httpCookie.getValue();
            }
        }
        //2:有 去缓存中查询是否存在用户ID   有：登录 没有：未登录
        if(!StringUtils.isEmpty(token)){
            //是否存在
            if(redisTemplate.hasKey(token)){
                return (String) redisTemplate.opsForValue().get(token);
            }
        }
        //3:没有 未登录
        return null;
    }

    //过滤器执行顺序   负整数最大值    0    正整数最大值
    //                 最优先执行    中     最后执行
    @Override
    public int getOrder() {
        return 0;
    }



}
